package ca.cbc.panacea.elixir.managed;

import ca.cbc.panacea.elixir.entity.PanaceaInstance;
import ca.cbc.panacea.elixir.exception.ElixirException;
import ca.cbc.panacea.elixir.service.PanaceaService;
import ca.cbc.panacea.elixir.util.ElixirProperties;
import ca.cbc.panacea.elixir.util.PanaceaDaemon;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.List;

/**
 * @author : Arash Bizhanzadeh <arash.bizhanzadeh@cbc.ca>
 *         Date: 11-11-17
 *         Time: 11:09 AM
 *         CBC.ca All rights reserved.
 */
@Named("panaceaController")
@SessionScoped
public class PanaceaController implements Serializable {
    private static final long serialVersionUID = -2535392284734878110L;
    private static Logger logger = LoggerFactory.getLogger(PanaceaController.class);

    @Inject
    private PanaceaService service;


    private PanaceaInstance instance;

    private transient UploadedFile file;
    private String fileContent;

    public UploadedFile getFile() {
        return file;
    }

    public void setFile(UploadedFile file) {
        this.file = file;
    }

    public List<PanaceaInstance> getInstances() {
        return service.getInstances();

    }

    public PanaceaInstance getInstance() {
        if (instance == null) {
            logger.debug("creating new instance");
            instance = new PanaceaInstance();
        }
        return instance;
    }

    public void setInstance(PanaceaInstance instance) {
        logger.debug("setting instance");
        this.instance = instance;

    }

    public String getFileContent() {
        return fileContent;
    }

    public String logout() {
        logger.debug("Logging out !!");
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "login";
    }

    public String save() {
        FacesMessage message = new FacesMessage();
        logger.debug("saving instance {}", instance);
        logger.debug("root directory is  {}", ElixirProperties.instance.getRootDir());
        try {
            String rootDir = ElixirProperties.instance.getDirectoryFor(instance);
            createAndCheck(rootDir);
            createAndCheck(rootDir + "/properties");
//            String c1 =SVNFacade.instance.createDir(instance.getRootDir());
//            FacesContext.getCurrentInstance().addMessage("",
//                    new FacesMessage("",c1));
//            String c2 = SVNFacade.instance.createDir(instance.getRootDir() + "properties");
//            FacesContext.getCurrentInstance().addMessage("",
//                    new FacesMessage("",c2));
            createAndCheck(instance.getOutputDir());
            createAndCheck(instance.getWorkDir());
            createAndCheck(instance.getDataDir());
            createAndCheck(instance.getTemplateDir());
//            String c3 = SVNFacade.instance.createDir(instance.getTemplateDir());
//            FacesContext.getCurrentInstance().addMessage("",
//                    new FacesMessage("", c3));
//

            writeLoggingConfigFile();
//            String s = SVNFacade.instance.addFile( instance.getRootDir() +"properties/log4j.properties");
//            SVNFacade.instance.commit(instance);
            service.save(instance);
//            FacesContext.getCurrentInstance().addMessage("",
//                    new FacesMessage("",s));

            message = new FacesMessage("", "Instance created successfully");
        } catch (ElixirException e) {
            message = new FacesMessage(e.getMessage());
            return "";
        } finally {
            FacesContext.getCurrentInstance().addMessage("", message);

        }
        return "instances";
    }

    private void createAndCheck(String fileString) throws ElixirException {
        File file = new File(fileString);
        if (!file.mkdir()) {
            if (!file.exists()) {
                throw new ElixirException("Could not create directory " + fileString);
            } else {
                FacesContext.getCurrentInstance().addMessage(""
                        , new FacesMessage(FacesMessage.SEVERITY_WARN, "Duplicate?", "The input/output directory already existed! " + fileString));
            }
        }

    }

    private void writeLoggingConfigFile() throws ElixirException {
        String content = "log4j.rootLogger=ERROR,R\n" +
                "\n" +
                "log4j.appender.stdout=org.apache.log4j.ConsoleAppender\n" +
                "log4j.appender.stdout.layout=org.apache.log4j.PatternLayout\n" +
                "\n" +
                "# Print the date in ISO 8601 format\n" +
                "log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] [%F:%L] : %m%n\n" +
                "\n" +
                "log4j.appender.R=org.apache.log4j.RollingFileAppender\n" +
                "log4j.appender.R.File=${log.dir}/${instance.name}/instance.log\n" +
                "\n" +
                "log4j.appender.R.MaxFileSize=10MB\n" +
                "# Keep one backup file\n" +
                "log4j.appender.R.MaxBackupIndex=1\n" +
                "\n" +
                "log4j.appender.R.layout=org.apache.log4j.PatternLayout\n" +
                "log4j.appender.R.layout.ConversionPattern=%d{ISO8601} %-5p [%t] [%F:%L] : %m%n\n" +
                "\n" +
                "log4j.logger.ca.cbc=info,R" +
                "\n";
        content = content.replaceAll("\\$\\{log\\.dir\\}", ElixirProperties.instance.getLoggingDir());
        content = content.replaceAll("\\$\\{instance.name\\}", instance.getDirectory());
        try {
            Files.append(content, new File(ElixirProperties.instance.getDirectoryFor(instance) + "/properties/log4j.properties"),
                    Charset.forName("UTF-8"));
        } catch (IOException e) {
            logger.error("could not write log4j properties file", e);
            throw new ElixirException("Could not write logging config file");
        }
    }

    public String delete() {
        FacesMessage msg = null;
        try {
            String p = PanaceaDaemon.probe(instance);
            logger.debug("Verifying status of instance");
            if (Strings.isNullOrEmpty(p)) {
                logger.debug("Process is not running");
            } else {
                logger.debug("process running with id {}", p);
                service.stop(instance);
            }
            //
            File file = new File(instance.getRootDir());
            service.delete(instance);
            Files.deleteRecursively(file);
            Files.deleteRecursively(new File(instance.getDataDir()));
            Files.deleteRecursively(new File(instance.getLoggingDir()));
//            SVNFacade.instance.delete(instance.getRootDir());
//            SVNFacade.instance.commit(instance);
            msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Info", "Deleted Successfully");
        } catch (ElixirException e) {
            msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", e.getMessage());

        } catch (IOException e) {
            msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "File System Access Error",
                    "Could not delete instances home directory at " + ElixirProperties.instance.getDirectoryFor(instance));
        } finally {
            FacesContext.getCurrentInstance().addMessage("", msg);
            logger.debug("Exiting probe command");
        }

        return "instances?faces-redirect=true";
    }

    public void saveProperties() {
        FacesMessage message = null;
        boolean isNew = true;
        try {
            File file = new File(instance.getPropertyFileName());
            if (file.exists()) {
                isNew = false;
                if (!file.delete()) {
                    message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "");
                    throw new ElixirException("Could not recreate the property file at " + instance.getPropertyFileName());
                }
            }
            file.getParentFile().mkdirs();
            Files.append(formatPair("ftp.base.url", instance.getProperties().getFtpUrl()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("ftp.username", instance.getProperties().getFtpUsername()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("ftp.password", instance.getProperties().getFtpPassword()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("new.file.handler.cron.expr", instance.getProperties().getFetchCron()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("publisher.one.cron.expr", instance.getProperties().getPublisherCron1()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("publisher.two.cron.expr", instance.getProperties().getPublisherCron2()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("publisher.three.cron.expr", instance.getProperties().getPublisherCron3()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("gsa.url", instance.getProperties().getGsaUrl()), file, Charset.forName("UTF-8"));
            Files.append(formatPair("gsa.datasource", instance.getProperties().getGsaDatasource()), file, Charset.forName("UTF-8"));
            logger.debug("Writing template dir {}", instance.getTemplateDir());
            Files.append(String.format("%s =%s\n", "template.base.dir",
                    instance.getTemplateDir()), file, Charset.forName("UTF-8"));

            logger.debug("Writing mapping file {}", instance.getMappingFile());
            Files.append(String.format("%s =%s\n", "mapping.file",
                    instance.getMappingFile()), file, Charset.forName("UTF-8"));
            logger.debug("Writing publisher file {}", instance.getPublisherFile());
            Files.append(String.format("%s =%s\n", "publisher.template.root",
                    instance.getTemplateDir()), file, Charset.forName("UTF-8"));
            logger.debug("Writing output dir {}", instance.getOutputDir());
            Files.append(String.format("%s =%s\n", "publisher.output.root",
                    instance.getOutputDir()), file, Charset.forName("UTF-8"));
            logger.debug("Writing output dir {}", instance.getOutputDir());
            Files.append(String.format("%s =%s\n", "hotfolder.dir",
                    instance.getDataDir()), file, Charset.forName("UTF-8"));
            Files.append(String.format("%s =%s\n", "publisher.file",
                    instance.getPublisherFile()), file, Charset.forName("UTF-8"));
            if (isNew) {
//                 String str = SVNFacade.instance.addFile(instance.getPropertyFileName());
//                 FacesContext.getCurrentInstance().addMessage("",
//                    new FacesMessage("",str));
            }
//            SVNFacade.instance.commit(instance);

            message = new FacesMessage(FacesMessage.SEVERITY_INFO, "", "Property file saved successfully.");

        } catch (ElixirException e) {
            logger.error(e.getMessage());
            message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "", e.getMessage());

        } catch (IOException e) {
            logger.error("could not write to file " + instance.getPropertyFileName(), e);
            message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "could not write to file " + instance.getPropertyFileName());

        } finally {
            if (message == null) {
                throw new RuntimeException("Message is null!!!");
            } else {
                FacesContext.getCurrentInstance().addMessage("", message);
            }
        }
    }

    private String formatPair(String name, String value) {
        return String.format("%s=%s\n", name, value);
    }

//    public String handleFileUpload() {
//        logger.debug("is file null {}", file == null);
//        FacesMessage msg = new FacesMessage("Succesful", getFile().getFileName() + " is uploaded.");
//        FacesContext.getCurrentInstance().addMessage(null, msg);
//        return "";
//    }

    public void handleMappingFileUpload(FileUploadEvent event) {
        uploadFile(event, "mapping");
    }

    public void handlePublishingFileUpload(FileUploadEvent event) {
        uploadFile(event, "publishing");
    }

    public void handleTemplateFileUpload(FileUploadEvent event) {
        uploadFile(event, "template");
    }

    private void uploadFile(FileUploadEvent event, String file) {
        logger.debug("upload upload upload");
        logger.debug("uploading {} file file {}", file, event.getFile().getFileName());
        FacesMessage msg = new FacesMessage("Successful", event.getFile().getFileName() + " is uploaded.");
        logger.debug("instance is {}", instance.getDirectory());
        File theFile;
        if (file.equals("mapping")) {
            theFile = new File(instance.getMappingFile());
        } else if (file.equals("publishing")) {
            theFile = new File(instance.getPublisherFile());
        } else {
            theFile = new File(instance.getTemplateDir() + "/" + event.getFile().getFileName());
        }
        try {
            logger.debug("Saving file {}", theFile.getCanonicalPath());
            Files.copy(ByteStreams.newInputStreamSupplier(event.getFile().getContents()), theFile);
            logger.debug("Adding {} to svn", theFile.getCanonicalPath());
//            if(!SVNFacade.instance.isInRepo(theFile.getCanonicalPath())){
//                SVNFacade.instance.addFile(theFile.getCanonicalPath());
//            }
//            SVNFacade.instance.commit(instance);

        } catch (IOException e) {
            msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "File Save Error", "Could not save the mapping file");
//        } catch (ElixirException e) {
//           FacesContext.getCurrentInstance().addMessage("",
//                   new FacesMessage(FacesMessage.SEVERITY_ERROR, "",e.getMessage()));
        }
        FacesContext.getCurrentInstance().addMessage("", msg);
    }

    public String reset() {
        logger.debug("Resetting the panacea instance ");
        instance = new PanaceaInstance();
        return "new";
    }

    public void start() {
        FacesMessage msg;
        if (!new File(instance.getMappingFile()).exists())
            msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Could not start instance.", "Missing the mapping file.  Please upload first.");
        else if (!new File(instance.getPublisherFile()).exists())
            msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Could not start instance.", "Missing the publisher file.  Please upload first.");
        else if (!new File(instance.getPropertyFileName()).exists())
            msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Could not start instance.", "Missing the properties.  Please configure first.");
        else
            msg = service.start(instance);
        FacesContext.getCurrentInstance().addMessage("", msg);
    }

    public void stop() {
        FacesMessage msg;
        msg = service.stop(instance);
        FacesContext.getCurrentInstance().addMessage("", msg);
    }

    public void probe() {
        FacesMessage message = null;
        try {
            String p = PanaceaDaemon.probe(instance);
            logger.debug("Probe completed");
            if (Strings.isNullOrEmpty(p)) {
                logger.debug("Process is not running");
                message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Stopped", "Process is not running ");
                service.setProcessId(instance, "");
            } else {
                logger.debug("process running with id {}", p);
                message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Running", "Process Running with pid "
                        + p);
                service.setProcessId(instance, p);
            }
        } catch (ElixirException e) {
            message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", e.getMessage());
        } finally {
            logger.debug("Exiting probe command");
            FacesContext.getCurrentInstance().addMessage(null, message);
        }

    }

    public void getMappingFile() {
        try {
            fileContent = Files.toString(new File(instance.getMappingFile()), Charset.forName("UTF-8"));
        } catch (IOException e) {
            logger.error("Error reading mapping file " + instance.getMappingFile(), e);
            FacesContext.getCurrentInstance().addMessage("",
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error reading File", e.getMessage()));
        }
    }

    public void getPublisherFile() {
        try {
            fileContent = Files.toString(new File(instance.getPublisherFile()), Charset.forName("UTF-8"));
        } catch (IOException e) {
            logger.error("Error reading mapping file " + instance.getMappingFile(), e);
            FacesContext.getCurrentInstance().addMessage("",
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error reading File", e.getMessage()));
        }
    }

    public void deleteTemplateFile() {
        String fileName = instance.getCurrentFileName().replace("template_", "");
        if (new File(instance.getTemplateDir() + fileName).delete()) {
            FacesContext.getCurrentInstance().addMessage("",
                    new FacesMessage(FacesMessage.SEVERITY_INFO, "Successful", fileName + " Deleted successfully"));
        } else {
            FacesContext.getCurrentInstance().addMessage("",
                    new FacesMessage(FacesMessage.SEVERITY_WARN, "Failed", fileName + " Could not be deleted"));
        }
    }
}
